package com.clei.utils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * byte 工具类
 *
 * @author Y
 * @date 2022-04-21
 */
public final class ByteUtil {

    /**
     * 16进制字符
     */
    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /**
     * 16进制大写字符
     */
    private static final char[] HEX_UPPER_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    /**
     * 只有一位为1的byte数组
     * 可以用于设置某位为1，通过使用位运算OR
     * 也可以用于判断某位是1或0，通过使用位运算AND
     */
    private static final byte[] SINGLE_BIT_ONE_ARR = new byte[]{1, 2, 4, 8, 16, 32, 64, -128};

    /**
     * 只有一位为0的byte数组
     * 可以用于设置某位为0，通过使用位运算AND
     * 也可以用于判断某位是1或0，通过使用位运算OR
     */
    private static final byte[] SINGLE_BIT_ZERO_ARR = new byte[]{-2, -3, -5, -9, -17, -33, -65, 127};

    /**
     * 将二字节的byte数组转为一个int
     *
     * @param data 源数据
     * @return int
     */
    public static int byte2ToInt(byte[] data) {
        return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
    }

    /**
     * 将四字节的byte数组转为一个int
     *
     * @param data 源数据
     * @return int
     */
    public static int byte4ToInt(byte[] data) {
        return ((data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16) | ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
    }

    /**
     * 将四字节的byte数组转为一个long
     *
     * @param data 源数据
     * @return long
     */
    public static long byte4ToLong(byte[] data) {
        return ((long) (data[0] & 0xFF) << 24) | ((data[1] & 0xFF) << 16) | ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
    }

    /**
     * 将8字节的byte数组转为一个long
     *
     * @param data 源数据
     * @return long
     */
    public static long byte8ToLong(byte[] data) {
        return ((long) (data[0] & 0xFF) << 56)
                | ((long) (data[1] & 0xFF) << 48)
                | ((long) (data[2] & 0xFF) << 40)
                | ((long) (data[3] & 0xFF) << 32)
                | ((long) (data[4] & 0xFF) << 24)
                | ((data[5] & 0xFF) << 16)
                | ((data[6] & 0xFF) << 8)
                | (data[7] & 0xFF);
    }

    /**
     * 将int转为二字节的byte数组
     *
     * @param i 源数据
     * @return byte[]
     */
    public static byte[] intTo2Byte(int i) {
        return new byte[]{(byte) ((i >>> 8) & 0xFF), (byte) (i & 0xFF)};
    }

    /**
     * 将int转为四字节的byte数组
     *
     * @param i 源数据
     * @return byte[]
     */
    public static byte[] intTo4Byte(int i) {
        return new byte[]{(byte) ((i >>> 24) & 0xFF), (byte) ((i >>> 16) & 0xFF), (byte) ((i >>> 8) & 0xFF), (byte) (i & 0xFF)};
    }

    /**
     * 将long转为4字节的byte数组
     *
     * @param l 源数据
     * @return byte[]
     */
    public static byte[] longTo4Byte(long l) {
        return new byte[]{(byte) ((l >>> 24) & 0xFF), (byte) ((l >>> 16) & 0xFF), (byte) ((l >>> 8) & 0xFF), (byte) (l & 0xFF)};
    }

    /**
     * 将long转为8字节的byte数组
     *
     * @param l 源数据
     * @return byte[]
     */
    public static byte[] longTo8Byte(long l) {
        return new byte[]{
                (byte) ((l >>> 56) & 0xFF),
                (byte) ((l >>> 48) & 0xFF),
                (byte) ((l >>> 40) & 0xFF),
                (byte) ((l >>> 32) & 0xFF),
                (byte) ((l >>> 24) & 0xFF),
                (byte) ((l >>> 16) & 0xFF),
                (byte) ((l >>> 8) & 0xFF),
                (byte) (l & 0xFF)};
    }

    /**
     * byte数组拼接
     *
     * @param bytesArr 多个byte数组
     * @return byte数组
     */
    public static byte[] merge(byte[]... bytesArr) {
        int length = 0;
        for (byte[] bytes : bytesArr) {
            length += bytes.length;
        }
        byte[] res = new byte[length];
        int from = 0;
        for (byte[] bytes : bytesArr) {
            System.arraycopy(bytes, 0, res, from, bytes.length);
            from += bytes.length;
        }
        return res;
    }

    private ByteUtil() {
    }

    /**
     * String -> byte[]
     *
     * @param str String
     * @return byte[]
     */
    public static byte[] toBytes(String str) {
        return toBytes(str, StandardCharsets.UTF_8);
    }

    /**
     * String -> byte[]
     *
     * @param str     String
     * @param charset Charset
     * @return byte[]
     */
    public static byte[] toBytes(String str, Charset charset) {
        return null == str ? null : str.getBytes(charset);
    }

    /**
     * byte[] -> String
     *
     * @param bytes byte数组
     * @return String
     */
    public static String toString(byte[] bytes) {
        return toString(bytes, StandardCharsets.UTF_8);
    }

    /**
     * byte[] -> String
     *
     * @param bytes   byte数组
     * @param charset Charset
     * @return String
     */
    public static String toString(byte[] bytes, Charset charset) {
        return null == bytes ? null : new String(bytes, charset);
    }

    /**
     * byte -> Hex String
     * upperCase
     *
     * @param b byte
     * @return String
     */
    public static String toHex(byte b) {
        return toHex(b, Boolean.TRUE);

    }

    /**
     * byte -> Hex String
     *
     * @param b         byte
     * @param upperCase 是否大写
     * @return String
     */
    public static String toHex(byte b, boolean upperCase) {
        char[] hd = upperCase ? HEX_UPPER_DIGITS : HEX_DIGITS;
        char[] arr = new char[2];
        arr[0] = hd[b >>> 4 & 0xF];
        arr[1] = hd[b & 0xF];
        return new String(arr);
    }

    /**
     * byte[] -> Hex String
     *
     * @param arr arr
     * @return String
     */
    public static String toHex(byte[] arr) {
        return toHex(arr, Boolean.TRUE);
    }

    /**
     * byte[] -> Hex String
     *
     * @param arr       arr
     * @param upperCase 是否大写
     * @return String
     */
    public static String toHex(byte[] arr, boolean upperCase) {
        char[] hd = upperCase ? HEX_UPPER_DIGITS : HEX_DIGITS;
        int length = arr.length;
        char[] str = new char[length * 2];
        for (int i = 0; i < length; i++) {
            byte b = arr[i];
            // 一个byte变两个char，前4位和后四位各一个
            str[i * 2] = hd[b >>> 4 & 0xF];
            str[i * 2 + 1] = hd[b & 0xF];
        }
        return new String(str);
    }

    /**
     * Hex String -> byte[]
     *
     * @param str 十六进制字符串
     * @return byte[]
     */
    public static byte[] hex2Bytes(String str) {
        char[] arr = str.toCharArray();
        int length = arr.length;
        if (NumUtil.isOdd(length)) {
            throw new RuntimeException("错误的字符串长度 " + length);
        }
        int byteLength = length / 2;
        byte[] data = new byte[byteLength];
        // 转换
        for (int i = 0; i < length; i++) {
            byte b1 = get16CharNum(arr[i]);
            byte b2 = get16CharNum(arr[++i]);

            byte b = (byte) ((b1 << 4) | b2);
            data[i / 2] = b;
        }
        return data;
    }

    /**
     * Hex String -> String
     *
     * @param str str
     * @return String
     */
    public static String hex2String(String str) {
        return toString(hex2Bytes(str));
    }

    /**
     * Hex String -> String
     *
     * @param str     str
     * @param charset charset
     * @return String
     */
    public static String hex2String(String str, Charset charset) {
        return toString(hex2Bytes(str), charset);
    }

    /**
     * 二进制字符串
     *
     * @param b byte
     * @return 二进制字符串
     */
    public static String toBinaryString(byte b) {
        return Integer.toBinaryString(b & 0XFF);
    }

    /**
     * 八位二进制字符串
     *
     * @param b byte
     * @return 八位二进制字符串
     */
    public static String toBinaryString8(byte b) {
        String str = toBinaryString(b);
        int max = 8;
        int length = str.length();
        int diff = max - length;
        if (0 != diff) {
            StringBuilder sb = new StringBuilder(max);
            for (int i = 0; i < diff; i++) {
                sb.append('0');
            }
            sb.append(str);
            return sb.toString();
        }
        return str;
    }

    /**
     * 取得b的第i位的值
     *
     * @param b 源数据
     * @param i bit位置 0到7
     * @return 0或1
     */
    public static int getBit(byte b, int i) {
        return 0 == (b & SINGLE_BIT_ONE_ARR[i]) ? 0 : 1;
        // return -1 == (b | SINGLE_BIT_ZERO_ARR[i]) ? 1 : 0;
    }

    /**
     * 将b的第i位设置v
     *
     * @param b 源数据
     * @param i bit位置 0到7
     * @param v 要设置的值 0或非0 非0一律当做1
     * @return 修改bit位后的值
     */
    public static byte setBit(byte b, int i, int v) {
        return 0 == v ? set0(b, i) : set1(b, i);
    }

    /**
     * 将b的第i位设置1
     *
     * @param b 源数据
     * @param i bit位置 0到7
     * @return 修改bit位后的值
     */
    public static byte set1(byte b, int i) {
        return (byte) (b | SINGLE_BIT_ONE_ARR[i]);
    }

    /**
     * 将b的多位设置1
     *
     * @param b    源数据
     * @param args bit索引数组 0到7
     * @return 修改bit位后的值
     */
    public static byte set1(byte b, int... args) {
        for (int i : args) {
            b = set1(b, i);
        }
        return b;
    }

    /**
     * 将b的第i位设置0
     *
     * @param b 源数据
     * @param i bit位置 0到7
     * @return 修改bit位后的值
     */
    public static byte set0(byte b, int i) {
        return (byte) (b & SINGLE_BIT_ZERO_ARR[i]);
    }

    /**
     * 将b的多位设置0
     *
     * @param b    源数据
     * @param args bit索引数组 0到7
     * @return 修改bit位后的值
     */
    public static byte set0(byte b, int... args) {
        for (int i : args) {
            b = set0(b, i);
        }
        return b;
    }

    /**
     * 将一个十进制的两位数转为一个8421码的BCD
     *
     * @param i 源数据
     * @return BCD
     */
    public static byte toBcd(int i) {
        if (i < 0 || i > 99) {
            throw new RuntimeException("参数错误 : " + i);
        }
        // 十位
        int a = i / 10;
        // 个位
        int b = i % 10;
        // 结果
        int c = (a << 4) | b;
        return (byte) c;
    }

    /**
     * 将一个8421码的BCD转为一个十进制的两位数
     *
     * @param b 源数据
     * @return int
     */
    public static int bcd2Int(byte b) {
        // 十位
        int a = b >> 4 & 0X0F;
        // 个位
        int aa = b & 0X0F;
        if (a > 9 || aa > 9) {
            throw new RuntimeException("错误的BCD : " + b);
        }
        return a * 10 + aa;
    }

    /**
     * crc16 1021
     *
     * @param arr arr
     * @return byte[]
     */
    public static int crc16(byte[] arr) {
        int crc = 0XFFFF;
        int polynomial = 0X1021;
        for (byte b : arr) {
            for (int j = 0; j < 8; ++j) {
                boolean bit = (b >> 7 - j & 1) == 1;
                boolean c15 = (crc >> 15 & 1) == 1;
                crc <<= 1;
                if (c15 ^ bit) {
                    crc ^= polynomial;
                }
            }
        }
        crc &= 0XFFFF;
        return crc;
    }

    /**
     * 读取bytes的一部分
     *
     * @param arr        arr
     * @param startIndex 开始index
     * @param length     长度
     * @return byte[]
     */
    public static byte[] readBytes(byte[] arr, int startIndex, int length) {
        byte[] target = new byte[length];
        System.arraycopy(arr, startIndex, target, 0, length);
        return target;
    }

    /**
     * 获取一个十六进制字符代表的数值
     *
     * @param c c
     * @return byte
     */
    private static byte get16CharNum(char c) {
        if ('0' <= c && '9' >= c) {
            return (byte) (c - '0');
        }
        if ('A' <= c && 'F' >= c) {
            return (byte) (c - 'A' + 10);
        }
        if ('a' <= c && 'f' >= c) {
            return (byte) (c - 'a' + 10);
        }
        throw new RuntimeException("错误的字符 " + c);
    }
}
